// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module A_cse_ocs_dpa_aes10c_state #(
          parameter     OCS_AES_DPA_USE_CTECH = 1,
	  parameter	AES_P = 1'b0
	)
	(
	  input logic sys_clk,
	  input logic reset_n,
	  input logic         encrypt,           // cypher operation - 1 => encrypt, 0 => decrypt
   	  input logic         mixcolumns,        // perform column mixing (or inverse for decryption)
	  input logic         bytesub_shiftrows, // perform byte substitution and row shifting (or inverses for decryption)
   	  input logic         add_roundkey,      // Added for AES', addround_key except for extra round,
   	  input logic [127:0] round_key,         // cypher round key
   	  input logic         aes_start,         // Start the AES operation
   	  input logic         store_state,       // Store the cipher value into the state
   	  input logic [127:0] data_in,           // Data Input bus (contains cipher-text/plain-text)
	  input logic [127:0] state_mask,	 // Mask added with the data
	  input logic [127:0] new_mask,		 // Output mask
   	  input logic         flush,             // Flush flops
	  input logic	      lastround,	 // Last round indicator to decompensate mask
   	  output logic [127:0] next_mask,	 // Mask for next round of encryption
	  output logic [127:0] data_out );        // Data Output bus (contains cipher-text/plain-text)

`include "A_cse_ocs_dpa_aes10c_localparams.v"
/**********************************************************************/
// Data Type Declarations (Signal Declarations)
/**********************************************************************/
   wire         decrypt;              // 1 => decrypt, 0 => encrypt
   wire [127:0] shiftrows_state;      // the state after row shifting
   wire [127:0] shiftrows_mask_in;      // the state mask after row shifting
   wire [127:0] gf_inverse_in;        // the input to the GF(256) inverse look-up table
   wire [127:0] gf_inverse_result;    // the output of the GF(256) inverse look-up table
   wire [127:0] gf_inverse_result_map;    // the output of the GF(256) inverse look-up table
   wire [127:0] sbox_in;             // the data inputs to sbox for encrypt and decrypt
   wire [127:0] mask_in;             // the mask inputs to sbox for encrypt and decrypt
   wire [127:0] subbytes;             // the output of the SubBytes function for encryption
   wire [127:0] state_stage2;         // state at the beginning of stage 2
   wire         encrypt_last_round;   // pulse that marks the last round of an encryption
   wire         decrypt_last_round;   // pulse that marks the last round of a decryption
   wire         encrypt_first_round;  // pulse that marks the first round of an encryption
   logic        decrypt_first_round;  // pulse that marks the first round of an encryption
   wire [127:0] mask_decompensate;

   wire [127:0] stateout;

   reg [127:0] encrypt_mask_out;      // next mask for encrypt
   reg [127:0] decrypt_mask_out;      // next mask for decrypt
   reg [127:0]  state;                // the 128-bit state register

   reg [127:0]  decrypt_addroundkey;  // the result of round key addition for decryption (stage 1)
   reg [127:0]  stage1_result;        // the result of stage 1 operations for encryption and decryption
   reg [127:0]  decrypt_data_in;     // decrypt key after state-xor and possible inverse-mix-cols
   reg [127:0]  decrypt_mask_in;     // decrypt state masj
   reg [127:0]  encrypt_addkey_in;    // the state input to the addround key operation for encryption (stage 2)
   reg [127:0]  stage2_result;        // the result of stage 2 operations for encryption and decryption

   logic [127:0] map_out;
   logic [127:0] inv_affine_out;

   wire         encrypt_update_iv;
   wire         decrypt_update_iv;

/**********************************************************************/
// Main code
/**********************************************************************/

assign data_out = stateout;

assign decrypt = !encrypt;

assign encrypt_last_round  = store_state &  !decrypt & !mixcolumns & bytesub_shiftrows;
assign decrypt_last_round  = store_state &   decrypt & !bytesub_shiftrows;
assign encrypt_first_round = store_state &  !decrypt & !bytesub_shiftrows;
assign decrypt_first_round = store_state &   decrypt & !mixcolumns & bytesub_shiftrows;

assign encrypt_update_iv = AES_P ? decrypt_last_round : encrypt_last_round;
assign decrypt_update_iv = AES_P ? encrypt_last_round : decrypt_last_round;


// Applying shiftrows to the state
assign shiftrows_state   = shiftrows(state);
assign shiftrows_mask_in = shiftrows(state_mask);

// Data
always_comb
   begin : enc_dec_inputs

   decrypt_addroundkey = add_roundkey ? state ^ round_key : state;

   if (mixcolumns)
	decrypt_data_in = {inverse_mix_column(decrypt_addroundkey[127:96]),
			   inverse_mix_column(decrypt_addroundkey[95:64 ]),
			   inverse_mix_column(decrypt_addroundkey[63:32 ]),
			   inverse_mix_column(decrypt_addroundkey[31:0  ])};
   else
	decrypt_data_in = decrypt_addroundkey;

   end

// Mask
always_comb
   begin : decrypt_mask
   
   if (mixcolumns)
	decrypt_mask_in = {inverse_mix_column(state_mask[127:96]),
                           inverse_mix_column(state_mask[95:64 ]),
                           inverse_mix_column(state_mask[63:32 ]),
                           inverse_mix_column(state_mask[31:0  ])};
    else
	decrypt_mask_in = state_mask;

   end

// Assign right inputs to Sbox
assign sbox_in = encrypt ? shiftrows_state       : decrypt_data_in;
assign mask_in = encrypt ? shiftrows_mask_in     : decrypt_mask_in;

// Instantiate the Masked Sbox modules
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH)) data_sbox15 ( .encrypt(encrypt), .in(sbox_in[127:120]), .in_mask(mask_in[127:120]), .out_mask(new_mask[127:120]), .out(subbytes[127:120]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox14 ( .encrypt(encrypt), .in(sbox_in[119:112]), .in_mask(mask_in[119:112]), .out_mask(new_mask[119:112]), .out(subbytes[119:112]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox13 ( .encrypt(encrypt), .in(sbox_in[111:104]), .in_mask(mask_in[111:104]), .out_mask(new_mask[111:104]), .out(subbytes[111:104]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox12 ( .encrypt(encrypt), .in(sbox_in[103:96 ]), .in_mask(mask_in[103:96 ]), .out_mask(new_mask[103:96 ]), .out(subbytes[103:96 ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox11 ( .encrypt(encrypt), .in(sbox_in[95:88  ]), .in_mask(mask_in[95:88  ]), .out_mask(new_mask[95:88  ]), .out(subbytes[95:88  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox10 ( .encrypt(encrypt), .in(sbox_in[87:80  ]), .in_mask(mask_in[87:80  ]), .out_mask(new_mask[87:80  ]), .out(subbytes[87:80  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox9  ( .encrypt(encrypt), .in(sbox_in[79:72  ]), .in_mask(mask_in[79:72  ]), .out_mask(new_mask[79:72  ]), .out(subbytes[79:72  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox8  ( .encrypt(encrypt), .in(sbox_in[71:64  ]), .in_mask(mask_in[71:64  ]), .out_mask(new_mask[71:64  ]), .out(subbytes[71:64  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox7  ( .encrypt(encrypt), .in(sbox_in[63:56  ]), .in_mask(mask_in[63:56  ]), .out_mask(new_mask[63:56  ]), .out(subbytes[63:56  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox6  ( .encrypt(encrypt), .in(sbox_in[55:48  ]), .in_mask(mask_in[55:48  ]), .out_mask(new_mask[55:48  ]), .out(subbytes[55:48  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox5  ( .encrypt(encrypt), .in(sbox_in[47:40  ]), .in_mask(mask_in[47:40  ]), .out_mask(new_mask[47:40  ]), .out(subbytes[47:40  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox4  ( .encrypt(encrypt), .in(sbox_in[39:32  ]), .in_mask(mask_in[39:32  ]), .out_mask(new_mask[39:32  ]), .out(subbytes[39:32  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox3  ( .encrypt(encrypt), .in(sbox_in[31:24  ]), .in_mask(mask_in[31:24  ]), .out_mask(new_mask[31:24  ]), .out(subbytes[31:24  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox2  ( .encrypt(encrypt), .in(sbox_in[23:16  ]), .in_mask(mask_in[23:16  ]), .out_mask(new_mask[23:16  ]), .out(subbytes[23:16  ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox1  ( .encrypt(encrypt), .in(sbox_in[15:8   ]), .in_mask(mask_in[15:8   ]), .out_mask(new_mask[15:8   ]), .out(subbytes[15:8   ]) );
A_cse_ocs_dpa_aes10c_gf16_sq_sbox_masked_withmap #(.OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH))data_sbox0  ( .encrypt(encrypt), .in(sbox_in[7:0    ]), .in_mask(mask_in[7:0    ]), .out_mask(new_mask[7:0    ]), .out(subbytes[7:0    ]) );

// Perform mix_columns for encryption

always_comb
   begin : enc_dec_preOutputs

	if (mixcolumns) begin
	   encrypt_addkey_in = {mix_column(subbytes[127:96]),
				mix_column(subbytes[95:64 ]),
				mix_column(subbytes[63:32 ]),
				mix_column(subbytes[31:0  ])};

	   encrypt_mask_out  = {mix_column(new_mask[127:96]),
				mix_column(new_mask[95:64 ]),
				mix_column(new_mask[63:32 ]),
				mix_column(new_mask[31:0  ])};
	end

	else if (bytesub_shiftrows) begin
	   encrypt_addkey_in = subbytes;
	   encrypt_mask_out = new_mask;
	end
	
	else begin
	   encrypt_addkey_in = state;
	   encrypt_mask_out = state_mask;
	end
   end

always_comb
   begin : enc_dec_outputs

	if (encrypt)
	  stage2_result = add_roundkey ? encrypt_addkey_in ^ round_key : encrypt_addkey_in;
	else begin
	  if (bytesub_shiftrows)
	     stage2_result = inverse_shiftrows(subbytes);
	  else
	     stage2_result = decrypt_addroundkey;
	end
   end

// Inverse_shift rows on mask_out for decrypt
always_comb
   begin : inverse_SR_mask

	if (decrypt) begin
	  if (bytesub_shiftrows)
		decrypt_mask_out = inverse_shiftrows(new_mask);
	  else
		decrypt_mask_out = state_mask;
	end

	else
		decrypt_mask_out = state_mask;

   end

assign next_mask = encrypt ? encrypt_mask_out : decrypt_mask_out;
assign mask_decompensate = (lastround) ? next_mask : {128{1'b0}};

assign stateout = stage2_result ^ mask_decompensate;

// Internal Registers
always_ff @(posedge sys_clk or negedge reset_n)
   begin : internal_registers

	if (~reset_n)
	  begin
	    state 	<= {128{1'b0}};
	  end

	else if (flush)
	  begin
	    state 	<= {128{1'b0}};
	  end

	else if (store_state | aes_start)
	  begin

	    if (aes_start)
	       begin
		  state 	<= data_in;
	       end

	    else if (store_state)
	       begin
		  state 	      <= stateout;
	       end
	  end

   end

// Functions used for state processing

/****************************************************************/
// Function name: shiftrows
// Inputs       : input_state[127:0]
// Description  : Applies the ShiftRows transformation to the
//                entire state.  Result is the 128-bit shifted state.
//
//                In the comments below, S and S' refer to the
//                input and shifted states respectively.  The indeces
//                r and c refer to row and column.
//
/****************************************************************/
function automatic [127:0] shiftrows;
  input [127:0] input_state;
  begin                                // S'rc <- Src
                                       // -----------
    shiftrows = {input_state[127:120], // S'00 <- S00  row 0 - no shift
                 input_state[87:80],   // S'10 <- S11  row 1 - left shift by 1
                 input_state[47:40],   // S'20 <- S22  row 2 - left shift by 2
                 input_state[7:0],     // S'30 <- S33  row 3 - left shift by 3
                 input_state[95:88],   // S'01 <- S01  row 0 - no shift
                 input_state[55:48],   // S'11 <- S12  row 1 - left shift by 1
                 input_state[15:8],    // S'21 <- S23  row 2 - left shift by 2
                 input_state[103:96],  // S'31 <- S30  row 3 - left shift by 3
                 input_state[63:56],   // S'02 <- S02  row 0 - no shift
                 input_state[23:16],   // S'12 <- S13  row 1 - left shift by 1
                 input_state[111:104], // S'22 <- S20  row 2 - left shift by 2
                 input_state[71:64],   // S'32 <- S31  row 3 - left shift by 3
                 input_state[31:24],   // S'03 <- S03  row 0 - no shift
                 input_state[119:112], // S'13 <- S10  row 1 - left shift by 1
                 input_state[79:72],   // S'23 <- S21  row 2 - left shift by 2
                 input_state[39:32]};  // S'33 <- S32  row 3 - left shift by 3
  end
endfunction // shiftrows

/****************************************************************/
// Function name: inverse_shiftrows
// Inputs       : input_state[127:0]
// Description  : Applies the Inverse ShiftRows transformation to the
//                entire state.  Result is the 128-bit shifted state.
//
//                In the comments below, S and S' refer to the
//                input and shifted states respectively.  The indeces
//                r and c refer to row and column.
//
/****************************************************************/
function automatic [127:0] inverse_shiftrows;
  input [127:0] input_state;
  begin                                // S'rc <- Src
                                       // -----------
    inverse_shiftrows = {input_state[127:120], // S'00 <- S00  row 0 - no shift
                 input_state[23:16],   // S'10 <- S13  row 1 - right shift by 1
                 input_state[47:40],   // S'20 <- S22  row 2 - right shift by 2
                 input_state[71:64],   // S'30 <- S31  row 3 - right shift by 3
                 input_state[95:88],   // S'01 <- S01  row 0 - no shift
                 input_state[119:112], // S'11 <- S10  row 1 - right shift by 1
                 input_state[15:8],    // S'21 <- S23  row 2 - right shift by 2
                 input_state[39:32],  // S'31 <- S32  row 3 - right shift by 3
                 input_state[63:56],   // S'02 <- S02  row 0 - no shift
                 input_state[87:80],   // S'12 <- S11  row 1 - right shift by 1
                 input_state[111:104], // S'22 <- S20  row 2 - right shift by 2
                 input_state[7:0],     // S'32 <- S33  row 3 - right shift by 3
                 input_state[31:24],   // S'03 <- S03  row 0 - no shift
                 input_state[55:48],   // S'13 <- S12  row 1 - right shift by 1
                 input_state[79:72],   // S'23 <- S21  row 2 - right shift by 2
                 input_state[103:96]}; // S'33 <- S30  row 3 - right shift by 3
  end
endfunction // inverse_shiftrows


/****************************************************************/
// Function name: mix_column
// Inputs       : input_column[31:0]
// Description  : Applies the MixColumns transformation
//                to a single input column per the AES standard.
//                Output is a single transformed column (4 bytes).
//
//                input_column is an array of bytes denoted as
//                {s0,s1,s2,s3}
//
//                The character 'x' in the signal names below
//                indicates multiplication in GF(256), so the
//                signal s0x2 is the input byte s0 multiplied
//                in GF(256) by 2.  Multiplication by three is
//                performed by adding the original input byte
//                to the input byte multiplied by 2.  The XOR
//                with the AES_GF256_POLY performs the modulo
//                reduction per the standard.
/****************************************************************/
function automatic [31:0] mix_column;
  input [31:0] input_column;
  reg [7:0] s0x2,s0x3;
  reg [7:0] s1x2,s1x3;
  reg [7:0] s2x2,s2x3;
  reg [7:0] s3x2,s3x3;
  begin

    if (input_column[7])
      s3x2 = (input_column[7:0] << 1) ^ AES_GF256_POLY;
    else
      s3x2 = input_column[7:0] << 1;
    s3x3 = s3x2 ^ input_column[7:0];

    if (input_column[15])
      s2x2 = (input_column[15:8] << 1) ^ AES_GF256_POLY;
    else
      s2x2 = input_column[15:8] << 1;
    s2x3 = s2x2 ^ input_column[15:8];

    if (input_column[23])
      s1x2 = (input_column[23:16] << 1) ^ AES_GF256_POLY;
    else
      s1x2 = input_column[23:16] << 1;
    s1x3 = s1x2 ^ input_column[23:16];

    if (input_column[31])
      s0x2 = (input_column[31:24] << 1) ^ AES_GF256_POLY;
    else
      s0x2 = input_column[31:24] << 1;
    s0x3 = s0x2 ^ input_column[31:24];

    mix_column  = {(s0x2                ^ s1x3                ^ input_column[15:8] ^ input_column[7:0]),
                   (input_column[31:24] ^ s1x2                ^ s2x3               ^ input_column[7:0]),
                   (input_column[31:24] ^ input_column[23:16] ^ s2x2               ^ s3x3             ),
                   (s0x3                ^ input_column[23:16] ^ input_column[15:8] ^ s3x2             )};
  end
endfunction // mix_column

/****************************************************************/
// Function name: inverse_mix_column
// Inputs       : input_column[31:0]
// Description  : Applies the InvMixColumns transformation
//                to a single input column per the AES standard.
//                Output is a single transformed column (4 bytes).
//
//                input_column is an array of bytes denoted as
//                {s0,s1,s2,s3}
//
//                The character 'x' in the signal names below
//                indicates multiplication in GF(256), so the
//                signal s0x2 is the input byte s0 multiplied
//                in GF(256) by 2.  The XOR with the AES_GF256_POLY
//                performs the modulo reduction per the standard.
//                Multiplication by other constants is performed by
//                summing intermediate factors.  For example,
//
//                s0x4 = s0x2 "times" 2 in GF(256)
//                s0x8 = s0x4 "times" 2 in GF(256)
//                s0x9 = s0x8 + s0
//
/****************************************************************/
function automatic [31:0] inverse_mix_column;
  input [31:0] input_column;
  reg [7:0] s0x2,s0x4,s0x8,s0x9,s0xb,s0xd,s0xe;
  reg [7:0] s1x2,s1x4,s1x8,s1x9,s1xb,s1xd,s1xe;
  reg [7:0] s2x2,s2x4,s2x8,s2x9,s2xb,s2xd,s2xe;
  reg [7:0] s3x2,s3x4,s3x8,s3x9,s3xb,s3xd,s3xe;
  begin

    if (input_column[7])
      s3x2 = (input_column[7:0] << 1) ^ AES_GF256_POLY;
    else
      s3x2 = input_column[7:0] << 1;
    if (s3x2[7])
      s3x4 =  (s3x2 << 1) ^ AES_GF256_POLY;
    else
      s3x4 = s3x2 << 1;
    if (s3x4[7])
      s3x8 = (s3x4 << 1) ^ AES_GF256_POLY;
    else
      s3x8 = s3x4 << 1;
    s3x9 = s3x8 ^ input_column[7:0];
    s3xb = s3x8 ^ s3x2 ^ input_column[7:0];
    s3xd = s3x8 ^ s3x4 ^ input_column[7:0];
    s3xe = s3x8 ^ s3x4 ^ s3x2;

    if (input_column[15])
      s2x2 = (input_column[15:8] << 1) ^ AES_GF256_POLY;
    else
      s2x2 = input_column[15:8] << 1;
    if (s2x2[7])
      s2x4 =  (s2x2 << 1) ^ AES_GF256_POLY;
    else
      s2x4 = s2x2 << 1;
    if (s2x4[7])
      s2x8 = (s2x4 << 1) ^ AES_GF256_POLY;
    else
      s2x8 = s2x4 << 1;
    s2x9 = s2x8 ^ input_column[15:8];
    s2xb = s2x8 ^ s2x2 ^ input_column[15:8];
    s2xd = s2x8 ^ s2x4 ^ input_column[15:8];
    s2xe = s2x8 ^ s2x4 ^ s2x2;

    if (input_column[23])
      s1x2 = (input_column[23:16] << 1) ^ AES_GF256_POLY;
    else
      s1x2 = input_column[23:16] << 1;
    if (s1x2[7])
      s1x4 =  (s1x2 << 1) ^ AES_GF256_POLY;
    else
      s1x4 = s1x2 << 1;
    if (s1x4[7])
      s1x8 = (s1x4 << 1) ^ AES_GF256_POLY;
    else
      s1x8 = s1x4 << 1;
    s1x9 = s1x8 ^ input_column[23:16];
    s1xb = s1x8 ^ s1x2 ^ input_column[23:16];
    s1xd = s1x8 ^ s1x4 ^ input_column[23:16];
    s1xe = s1x8 ^ s1x4 ^ s1x2;

    if (input_column[31])
      s0x2 = (input_column[31:24] << 1) ^ AES_GF256_POLY;
    else
      s0x2 = input_column[31:24] << 1;
    if (s0x2[7])
      s0x4 =  (s0x2 << 1) ^ AES_GF256_POLY;
    else
      s0x4 = s0x2 << 1;
    if (s0x4[7])
      s0x8 = (s0x4 << 1) ^ AES_GF256_POLY;
    else
      s0x8 = s0x4 << 1;
    s0x9 = s0x8 ^ input_column[31:24];
    s0xb = s0x8 ^ s0x2 ^ input_column[31:24];
    s0xd = s0x8 ^ s0x4 ^ input_column[31:24];
    s0xe = s0x8 ^ s0x4 ^ s0x2;

    inverse_mix_column  = {(s0xe ^ s1xb ^ s2xd ^ s3x9),
                           (s0x9 ^ s1xe ^ s2xb ^ s3xd),
                           (s0xd ^ s1x9 ^ s2xe ^ s3xb),
                           (s0xb ^ s1xd ^ s2x9 ^ s3xe)};
  end
endfunction // inverse_mix_column

endmodule
